package unite;


import java.io.Serializable;
import java.util.ArrayList;

import regles.Regle;
import terrain.CaseTerrain;
import deroulementjeu.Equipe;


/**
 * Classe de gestion d'une unité
 * @author Alexis BRASY, Geoffrey LEQUESNE, Sandrine PIRIOU, Michel POPOV
 */
public abstract class Unite implements Serializable {
    private static final long serialVersionUID = 1L;
	
    /** Nombre de PV de l'unité */
    protected int pv;

    /** Nombre de points de déplacement de l'unité */
    protected int deplacement;

    /** Taux de chance d'une unité */
    protected float chance;

    /** Equipe à laquelle appartient l'unité */
    protected Equipe equipe;

    /** Case terrain sur laquelle est positionnée l'unité */
    protected CaseTerrain caseTerrain;

    /** Stratégie de l'unité (si elle fait partie de l'équipe de l'IA) */
    protected TypeStrategie strategie = TypeStrategie.NONDEFINIE;
	
    /**
     * Accesseur au type de l'unité
     * @return le type de l'unité
     */
    public abstract TypeUnite getType();
	
    /**
     * Accesseur de l'attribut PV
     * @return les PV d'une unité
     */
    public int getPv() {
        return pv;
    }

    /**
     * Fixe les PV d'une unité
     * @param pv PV de l'unité
     */
    public void setPv(int pv) {
        this.pv = pv;
    }
	
    /**
     * Accesseur de la case de terrain de l'unité
     * @return la case de terrain sur laquelle est positionnée l'unité
     */
    public CaseTerrain getCaseTerrain() {
        return caseTerrain;
    }
	
    /**
     * Mutateur de la caseTerrain sur laquelle se trouve une unité.
     * @param caseTerrain Nouvelle valeur
     */
    public void setCaseTerrain(CaseTerrain caseTerrain) {
        this.caseTerrain = caseTerrain;
    }

    /**
     * Fixer la case terrain sur laquelle est positionnée l'unité
     * @param ct la case de terrain sur laquelle on veut placer l'unité
     */
    public void placer(CaseTerrain ct) {
        this.caseTerrain = ct;
    }

    /**
     * Retourne le déplacement d'une unité
     * @return le nombre de points de déplacement de l'unité
     */
    public int getDeplacement() {
        return deplacement;
    }
	
    /**
     * Fixe la valeur de point de déplacement
     * @param deplacement la valeur du nombre de points de déplacement d'une unité
     */
    public void setDeplacement(int deplacement) {
        this.deplacement = deplacement;
    }
	
    /**
     * Accesseur à la chance d'une unité
     * @return la chance d'une unité
     */
    public float getChance() {
        return chance;
    }

    /**
     * Réinitialise une unité au debut de chaque tour
     */
    public abstract void initTour();

    /**
     * Detruis l'unité 
     */
    public void tuer() {
        equipe.retirerUnite(this);
        if (caseTerrain.getUnite() == this) {
            caseTerrain.retirerUnite();
        }
    }
	
    /**
     * Initialisation de l'attribut chance d'une unité (abstrait)
     */
    public abstract float initChance();
	
    /**
     * Retourne la capacite d'une unité à riposter
     */
    public boolean peutRiposter() {
        return true;
    }
	
    /**
     * Ajouter l'unité à une équipe
     * @param equipe l'équipe de l'unité
     */
    public void ajouterEquipe(Equipe equipe) {
        this.equipe = equipe;
    }

    protected boolean mort() {
        if (pv > 0) {
            return false;
        } else {
            return true;
        }
    }

    protected int lanceCoup(Unite attaque) {
        float degats_int;

        // degats_int = attA + attA * (fact impact+chanceA)
        degats_int = getAttaque()
                + getAttaque()
                        * (this.getRegles().getFacteurImpact(this.getType(),
                        attaque.getType())
                                + this.getChance());
		
        assert attaque.getDefense() <= 23;
		
        int pertePV = (int) (degats_int
                * (1
                        - (0.7 * attaque.getDefense() / 23.0
                                + 0.3 * attaque.getChance())));

        assert pertePV > 0;
        attaque.pertePV(pertePV);
        return pertePV;
    }

    /**
     * Accesseur de la capacité d'attaque.
     * @return Nombre de points d'attaque.
     */
    public abstract int getAttaque();

    public int getDefense() {
        return getAttaque();
    }

    /**
     * Accesseur sur l'équipe à laquelle appartient l'unité.
     * @return Equipe
     */
    public Equipe getEquipe() {
        return equipe;
    }

    /**
     * Infliger une perte de PV à l'unité.
     * @param nb Nombre de PV que l'unité perd
     */
    public void pertePV(int nb) {
        pv = pv - nb;
        assert nb >= 0;
        
		
    }

    /**
     * Obtenir les règles qui régissent la partie.
     * @return Regle
     */
    public Regle getRegles() {
        return this.getCaseTerrain().getTerrain().getRegles();
    }
	
    abstract public int getXp();
	
    /**
     * Retourne le niveau de l'unité. Retourne 0 si N/A.
     * @return int supérieur ou égal à 0
     */
    abstract public int getNiveau();
	
    /**
     * Initialise la stratégie aléatoirement
     */
    public void initStrategie() {
        int i = (int) (Math.random() * 10);

        if (i < 5) {
            strategie = TypeStrategie.DEFENSIVE;
            
        } else {
            strategie = TypeStrategie.OFFENSIVE;
            
        }
    }
	
    public TypeStrategie getStrategie() {
        return strategie;
    }
	
    /**
     * Accesseur sur la taille du champ de vision d'une unité.
     * @return Taille du champ de vision.
     */
    abstract public int getVision();
	
    /**
     * Accesseur sur la qualité Pokémon de l'unité.
     * @return true si l'unité est un Pokémon, false sinon.
     */
    public boolean estPokemon() {
        ArrayList<TypeUnite> typesPokemon = new ArrayList<TypeUnite>();

        typesPokemon.add(TypeUnite.EAU);
        typesPokemon.add(TypeUnite.ELECTRIQUE);
        typesPokemon.add(TypeUnite.FEU);
        typesPokemon.add(TypeUnite.PLANTE);
        typesPokemon.add(TypeUnite.VOL);
		
        if (typesPokemon.contains(getType())) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * Accesseur sur la qualité Objet de l'unité.
     * @return true si l'unité est un Objet, false sinon.
     */
    public boolean estObjet() {
        ArrayList<TypeUnite> typesObjet = new ArrayList<TypeUnite>();

        typesObjet.add(TypeUnite.OBSTACLE);
        typesObjet.add(TypeUnite.POTION);
        typesObjet.add(TypeUnite.TELEPORTEUR);
		
        if (typesObjet.contains(getType())) {
            return true;
        } else {
            return false;
        }
    }
	
    /**
     * Accesseur sur la qualité Chanceux de l'unité.
     * @return true si l'unité est chanceuse, false sinon.
     */
    public boolean estChanceux() {
        if (chance >= 0.3f) {
            return true;
        } else {
            return false;
        }
    }
	
    /**
     * Accesseur sur la qualité "presque mort" de l'unité. Une unité presque morte ne peut plus se déplacer autant qu'elle veut.
     * @return true si l'unité est chanceuse, false sinon.
     */
    public boolean estPresqueMort() {
        if (!this.estPokemon()) {
            return false;
        }
        if (getRegles().getXpMortel() <= 0) {
            return false;
        }
        if (this.estPokemon() && this.getXp() >= getRegles().getXpMortel()) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * Méthode à appeler en fin de tour, pour régénérer les PV en fonction des règles, si le pokémon ne s'est pas déplacé
     */
    abstract public void regenPVfinTour();
	
    abstract public String getName();
	
}
